/*
 * Copyright 2011 Subhabrata Ghosh <subho.ghosh at langene.net>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



package net.langene.nlp.training.en;

//~--- non-JDK imports --------------------------------------------------------

import net.langene.common.Job;
import net.langene.common.JobManager;
import net.langene.common.JobStatus;
import net.langene.common.ProgramUtils;
import net.langene.nlp.Context;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//~--- JDK imports ------------------------------------------------------------

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

import java.util.HashMap;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import net.langene.nlp.training.Trainer;

//~--- classes ----------------------------------------------------------------

/**
 * Perform training based on the OpenANC corpus.
 *
 * @author Subhabrata Ghosh <subho.ghosh at langene.net>
 */
public class OpenANCTrainer implements Trainer {
    private static Logger	_logger = LoggerFactory.getLogger(OpenANCTrainer.class);

    //~--- fields -------------------------------------------------------------

    private boolean		isrunning = false;
    private final Queue<String>	filelist  = new LinkedBlockingQueue<String>();

    //~--- methods ------------------------------------------------------------

    @Override
    public void setup() throws Exception {
        String	prop = Context.get().getConfig().getString("net.langene.training.openanc.sources");

        if ((prop != null) &&!prop.isEmpty()) {
            loadTrainingFiles(prop);
        } else {
            loadTrainingFiles();
        }
    }

    private void loadTrainingFiles() throws Exception {
        String	datadir =
            Context.get().getConfig().getString("net.langene.training.corpus.directory");
        File	di = new File(datadir);

        if (!di.exists()) {
            throw new Exception("Specified directory [" + datadir
                                + "] cannot be found or isn't a directory.");
        }

        loadFiles(di.getAbsolutePath());
    }

    private void loadTrainingFiles(String listfile) throws Exception {
        String	datadir =
            Context.get().getConfig().getString("net.langene.training.corpus.directory");
        File		ddi = new File(datadir);
        BufferedReader	br  = new BufferedReader(new FileReader(listfile));

        while (true) {
            String	line = br.readLine();

            if (line == null) {
                break;
            }

            if (line.isEmpty() || line.trim().startsWith("#")) {
                continue;
            }

            String	dirname = ddi.getAbsolutePath() + "/" + line;
            File	dn      = new File(dirname);

            if (!dn.exists()) {
                _logger.warn("Invalid directory specified [" + dn.getAbsolutePath() + "]");

                continue;
            }

            loadFiles(dn.getAbsolutePath());
        }

        br.close();
    }

    private void loadFiles(String directory) throws Exception {
        String	datadir =
            Context.get().getConfig().getString("net.langene.training.corpus.directory");
        File	ddi = new File(datadir);
        File	di  = new File(directory);

        if (!di.isDirectory()) {
            return;
        }

        String[]	files = di.list();

        if ((files != null) && (files.length > 0)) {
            for (String file : files) {
                File	fi = new File(directory + "/" + file);

                if (fi.isDirectory()) {
                    loadFiles(fi.getAbsolutePath());
                }

                if (!fi.getName().endsWith(".txt")) {
                    continue;
                }

                String	fname = fi.getAbsolutePath().replaceFirst(".txt$", "");

                // Check if Sentence splitter file exists.
                String	fsname = fname + "-s.xml";
                File	fs     = new File(fsname);

                if (!fs.exists()) {
                    continue;
                }

                // Check if Hepple Annotations exists.
                String	fhname = fname + "-hepple.xml";
                File	fh     = new File(fhname);

                if (!fh.exists()) {
                    continue;
                }

                String	regex = ddi.getAbsolutePath().replaceAll("\\\\", "\\\\\\\\");

                fname = fname.replaceFirst(regex, "");
                fname = fname.replaceAll("\\\\", "/");

                if (!fname.startsWith("/")) {
                    fname = "/" + fname;
                }

                filelist.add(fname);

                // _logger.debug("Added OpenANC file [" + fname + "] to be processed...");
            }
        }
    }

    @Override
    @SuppressWarnings("SleepWhileInLoop")
    public void train(int threads) throws Exception {
        HashMap<String, OpenANCTrainingJob>	jindx = new HashMap<String, OpenANCTrainingJob>();

        isrunning = true;

        for (int ii = 0; ii < threads; ii++) {
            OpenANCTrainingJob	job = new OpenANCTrainingJob(this);

            jindx.put(job.getId(), job);
            JobManager.get().add(job);
        }

        while (true) {
            if (!isrunning || filelist.isEmpty()) {
                isrunning = false;

                break;
            }

            List<String>	fjobids = JobManager.get().getFinishedJobs(jindx.keySet());

            if (!fjobids.isEmpty()) {
                for (String jobid : fjobids) {
                    OpenANCTrainingJob	job = jindx.get(jobid);

                    if (job.getStatus() == JobStatus.Exception) {
                        _logger.error("Error processing input file [" + job.getSource() + "] -> "
                                      + job.getException().getLocalizedMessage());
                        ProgramUtils.printStacktrace(_logger, job.getException());
                    }

                    JobManager.get().add(job);
                }
            }

            Thread.sleep(1000);
        }

        for (String key : jindx.keySet()) {
            Job<?>	job = jindx.get(key);

            job.dispose();
        }
    }

    //~--- get methods --------------------------------------------------------

    /**
     * Get the next content source file in the queue.
     *
     * @return Source name, or null if no more files left.
     * @throws Exception
     */
    public String getSource() throws Exception {
        synchronized (filelist) {
            if (!filelist.isEmpty()) {
                return filelist.remove();
            }
        }

        return null;
    }

    //~--- methods ------------------------------------------------------------

    @Override
    public void terminate() {
        isrunning = false;
    }

    //~--- get methods --------------------------------------------------------

    /**
     *
     * @return
     */
    @Override
    public boolean isRunning() {
        return isrunning;
    }

    //~--- methods ------------------------------------------------------------

    @Override
    public void dispose() {}
}


//~ Formatted by Jindent --- http://www.jindent.com
